﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using Pixysoft.Framework.Configurations;
using Pixysoft.Framework.Remoting.Controller;
using Pixysoft.Net.Crawler;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Remoting.Core
{
    class RemoteChannel<T> : RealProxy, IRemoteChannel<T>
    {
        private static object syncRoot = new Object();

        string token = null;
        string url;
        Type type = null;
        List<HttpPoster> posters = new List<HttpPoster>();

        public RemoteChannel(string url)
            : base(typeof(T))
        {
            this.url = url;
            this.type = typeof(T);
        }

        public RemoteChannel(string url, string token)
            : base(typeof(T))
        {
            this.url = url;
            this.token = token;
            this.type = typeof(T);
        }

        public string Login(string username, string password)
        {
            if (!Verification.Verifies(username, password).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(username, password);

            string md5password = Pixysoft.Security.MD5.GetMD5(password);

            string request = CommandController.Instance.CreateLoginRequest(username, md5password);

            string xmlresponse = GetXmlResponse(url, request);

            IXmlNode response = CommandController.Instance.Deserialize(xmlresponse);

            if (response == null)
                throw Exceptions.UnexpectedResultException(xmlresponse);

            this.token = response.Text;

            return token;
        }

        public bool IsLogin()
        {
            return !string.IsNullOrEmpty(token);
        }

        public void Logout()
        {
            if (string.IsNullOrEmpty(token))
                throw Exceptions.UnexpectedResultException("please login first");

            string xmlresponse = GetXmlResponse(url, CommandController.Instance.CreateSimpleRequest(token, RemoteCommand.Logout.ToString(), null));
        }

        public void Dispose()
        {
            foreach (HttpPoster poster in posters.ToArray())
            {
                try
                {
                    poster.Close();
                }
                catch
                {
                }
            }

            lock (syncRoot)
            {
                this.posters.Clear();
            }
        }

        public T RemoteProxy
        {
            get { return (T)this.GetTransparentProxy(); }
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage methodCall = msg as IMethodCallMessage;

            MethodInfo method = methodCall.MethodBase as MethodInfo;

            if (method == null)
                return new ReturnMessage(Exceptions.UnexpectedResultException("only support method."), methodCall);

            if (string.IsNullOrEmpty(token))
                return new ReturnMessage(Exceptions.UnexpectedResultException("please login first."), methodCall);

            if (method == typeof(object).GetMethod("GetType"))
            {
                return new ReturnMessage(type, null, 0, null, methodCall);
            }
            else if (method == typeof(object).GetMethod("Equals", new Type[] { typeof(object) }))
            {
                return new ReturnMessage(false, null, 0, null, methodCall);
            }
            else if (method == typeof(object).GetMethod("GetHashCode"))
            {
                return new ReturnMessage(this.GetHashCode(), null, 0, null, methodCall);
            }
            else if (method == typeof(object).GetMethod("ToString"))
            {
                return new ReturnMessage(this.ToString(), null, 0, null, methodCall);
            }

            //调用序列化

            IXmlNode node = null;

            try
            {
                node = ReflectionController.Instance.GetMethodDeclaration(method, methodCall.Args);
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, methodCall);
            }

            string rawrequest = node.Serialize();

            string compresscontent = Pixysoft.IO.Compressor.Instance.Compress(rawrequest);
            string encryptcontent = Pixysoft.Security.DES.Encrypt(compresscontent, StringHelper.global_framework_id);
            string base64content = Pixysoft.Security.Base64.Encript(encryptcontent);
            IXmlNode request = CommandController.Instance.CreateRequest(token, RemoteCommand.RemoteCall.ToString(), base64content);


            string rawresponse = null;

            //远程访问

            try
            {
                string xmlresponse = GetXmlResponse(url, request.Serialize());

                base64content = CommandController.Instance.Deserialize(xmlresponse).Text;

                if (string.IsNullOrEmpty(base64content))
                {
                    rawresponse = null;
                }
                else
                {
                    encryptcontent = Pixysoft.Security.Base64.Decript(base64content);
                    compresscontent = Pixysoft.Security.DES.Decrypt(encryptcontent, StringHelper.global_framework_id);
                    rawresponse = Pixysoft.IO.Compressor.Instance.DeCompress(compresscontent);
                }
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, methodCall);
            }


            //返回远程结果

            if (method.ReturnType.Equals(typeof(void)))
                return new ReturnMessage(null, null, 0, null, methodCall);

            object returnvalue = null;

            try
            {
                returnvalue = ReflectionController.Instance.GetDeserializeRemoteCallResult(method.ReturnType, rawresponse);
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, methodCall);
            }

            return new ReturnMessage(returnvalue, null, 0, null, methodCall);
        }

        private string GetXmlResponse(string url, string message)
        {
            LoggerHelper.Debug("CP.REQUEST:" + message);

            HttpResult result = null;

            HttpPoster poster = new HttpPoster();

            posters.Add(poster);

            try
            {
                result = poster.Post(url, message);
            }
            catch (System.Net.WebException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (System.InvalidOperationException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (System.Threading.ThreadAbortException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (Exception ex)
            {
                LoggerHelper.Info(ex.Message);

                if (Exceptions.IsHandled(ex))
                    throw ex;

                throw Exceptions.Parse(ex);
            }
            finally
            {
                posters.Remove(poster);
            }

            if (!CommandController.Instance.IsSucceed(result.Response))
                throw Exceptions.ExternalException(result.Response);

            LoggerHelper.Debug("CP.RESPONSE:" + result.Response);

            return result.Response;
        }

    }
}
